#include "Common.fx"
#include "Sampling.fx"

//--------------------------------------
struct a2v {
	a2vApplyBasic basic;
};

struct v2f {
	v2fApplyBasic basic;
};

//--------------------------------------
sampler ProjLightMapSamp : register(s2);
sampler ShadowMapFullSamp : register(s3);
sampler OccluderMapSamp : register(s5);

const float g_ReducedShadowMapSize				: REDUCED_SHADOWMAP_SIZE;
const float2 g_InvReducedShadowMapSize			: INV_REDUCED_SHADOWMAP_SIZE;
const float2 g_InvOccluderMapSize				: INV_OCCLUDER_MAP_SIZE;

//--------------------------------------
v2f vsMain(a2v input) {
	
	v2f output;
	
	ApplyBasicVertexProcess(output.basic, input.basic);
	
	return output;
}

//--------------------------------------
f2fb psMain(v2f input) {

	f2fb output;
	
	input.basic.lsPos.xy /= input.basic.lsPos.w;
	input.basic.lsPos.xy = saturate(input.basic.lsPos.xy);
	
	float4 lightMap = tex2D(ProjLightMapSamp, input.basic.lsPos.xy);
	
	if(input.basic.lsPos.z < 0.01)
		lightMap = 0;
	
	float2 smTexcoordFull, smTexcoordOccluder;
	ShadowMapBaseTexcoordEx(smTexcoordFull, input.basic.lsPos.xy, g_InvShadowMapSize.y);
	ShadowMapBaseTexcoordEx(smTexcoordOccluder, input.basic.lsPos.xy, g_InvOccluderMapSize.y);
	
	float4 smValsFull[4];
	float4 smValsReduced[4];
	
	float4 smResultsFull[4];
	float4 smResultsReduced[4];
	
	// Fetch Samples
	float4 sample;
	
	// Fetch from down-sampled shadow map
	for(int i=0; i<4; ++i)
	{
		for(int j=0; j<4; ++j)
		{	
			FetchOffsetedSampleEx( sample, ShadowMapFullSamp, smTexcoordFull, g_Poisson[i * 4 + j], g_InvShadowMapSize.x * 8 );
			smValsReduced[i][j] = sample.x;
		}
		
		// compare 4 samples
		smResultsReduced[i] = input.basic.lsPos.z < smValsReduced[i];
	}
	
	// Fetch from full-sized shadow map
	for(int i=0; i<4; ++i)
	{
		for(int j=0; j<4; ++j)
		{	
			FetchOffsetedSampleEx( sample, ShadowMapFullSamp, smTexcoordFull, g_Poisson[i * 4 + j], g_InvShadowMapSize.x );
			smValsFull[i][j] = sample.x;
		}
		
		// compare 4 samples
		smResultsFull[i] = input.basic.lsPos.z < smValsFull[i];
	}
	
	// find occluder Depth
	float occluderDepth = tex2Dlod( OccluderMapSamp, float4(smTexcoordOccluder, 0, 0) ).y;
	float dist2Occluder = saturate(input.basic.lsPos.z - occluderDepth) / occluderDepth;
	dist2Occluder = saturate(dist2Occluder * g_VarPCFDistFactor);
	
	float shadowResult = 1;
	
	if(occluderDepth < input.basic.lsPos.z)
	{
		float2 shadowTerm = 0;
		
		float4 tmp;
		
		for(int i=0; i<4; ++i)
		{
			float4 resultsInter = lerp(smResultsFull[i], smResultsReduced[i], dist2Occluder);
			tmp[i] = dot(resultsInter, 0.25);	
		}
		
		shadowResult = dot(tmp, 0.25);
		
		//BilinearInterpolateResults4x4(shadowTerm.x, smResultsFull, lerpWeightFull);
		//BilinearInterpolateResults4x4(shadowTerm.y, smResultsReduced, lerpWeightReduced);
		
		//shadowResult = lerp(shadowTerm.x, shadowTerm.y, dist2Occluder);
	}
	
	lightMap *= shadowResult;
	
	ApplyBasicFragmentProcess(output.c0, input.basic, lightMap);
	
#ifdef DEBUG_VARPCF
	output.c0 = (occluderDepth - input.basic.lsPos.z);
#endif

	return output;
}

//--------------------------------------
technique Apply {

	pass {
	
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		
		MagFilter[5]		= Linear;
		MinFilter[5]		= Linear;
		
		VertexShader		= compile vs_3_0 vsMain();
		PixelShader			= compile ps_3_0 psMain();
	}
}

//--------------------------------------